﻿using System;
using System.Diagnostics;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Babylon.Toolbox
{
    [DebuggerDisplay("{Name}")]
    public class EffectParameter
    {
        public string Name { get; internal set; }
        public int VertexShaderRegisterIndex { get; internal set; }
        public int PixelShaderRegisterIndex { get; internal set; }
        public int RegisterCount { get; internal set; }

        bool isDirty = true;

        readonly Effect effect;
        Vector4[] data;

        Vector4[] Data
        {
            set
            {
                if (value == null && data == null)
                    return;

                if (value != null && data != null && value.Length == data.Length)
                {
                    if (!data.Where((t, index) => value[index] != t).Any())
                        return;
                }

                isDirty = true;
                data = value; 
            }
        }

        internal EffectParameter(Effect effect, string name)
        {
            this.effect = effect;
            Name = name;
            VertexShaderRegisterIndex = -1;
            PixelShaderRegisterIndex = -1;
            RegisterCount = 1;
        }

        public void SetValue(float single)
        {
            Data = new[] { new Vector4(single,0, 0, 0) };
            
        }
        public void SetValue(Vector2 vector2){
            Data = new[]{new Vector4(vector2, 0, 0)};
        }
        public void SetValue(Vector3 vector3)
        {
            Data = new[] { new Vector4(vector3, 0),  };
        }

        public void SetValue(Vector4 vector4)
        {
            Data = new[] {vector4};
        }

        public void SetValue(Matrix matrix)
        {
            Data = matrix.ToVector4Array();
        }

        public void SetValue(Matrix[] matrices)
        {
            var tempData = new Vector4[4 * matrices.Length];
            for (int i = 0; i < matrices.Length; i++)
            {
                tempData[4 * i] = new Vector4(matrices[i].M11, matrices[i].M12, matrices[i].M13, matrices[i].M14);
                tempData[1 + 4 * i] = new Vector4(matrices[i].M21, matrices[i].M22, matrices[i].M23, matrices[i].M24);
                tempData[2 + 4 * i] = new Vector4(matrices[i].M31, matrices[i].M32, matrices[i].M33, matrices[i].M34);
                tempData[3 + 4 * i] = new Vector4(matrices[i].M41, matrices[i].M42, matrices[i].M43, matrices[i].M44);
            }
            Data = tempData;
        }

        public void SetValue(float float01, float float02, float float03, float float04)
        {
            Data = new[] {new Vector4(float01, float02, float03, float04)};
        }

        public void SetValue(bool bool01, bool bool02, bool bool03, bool bool04)
        {
            Data = new[] {new Vector4(bool01 ? 1 : 0, bool02 ? 1 : 0, bool03 ? 1 : 0, bool04 ? 1 : 0)};
        }

        public void SetValue(Color color, float alpha)
        {
            Data = new[]{new Vector4(color.R / 255.0f, color.G / 255.0f, color.B / 255.0f, alpha)};
        }

        public void SetValue(Color color)
        {
            Data = new[] { new Vector4(color.R / 255.0f, color.G / 255.0f, color.B / 255.0f, color.A / 255.0f) };
        }

        internal void Apply()
        {
            if (data == null)
                return;

            int size = Math.Min(RegisterCount, data.Length);

            // Checking dirty state
            if (!isDirty)
            {
                bool isReallyDirty = false;

                if (VertexShaderRegisterIndex >= 0)
                {
                    isReallyDirty = EffectCacheSystem.DoIOwnVertexRegisters(effect, VertexShaderRegisterIndex, size);
                }

                if (isReallyDirty && PixelShaderRegisterIndex >= 0)
                {
                    if (EffectCacheSystem.DoIOwnPixelRegisters(effect, PixelShaderRegisterIndex, size))
                        return;
                }
            }

            for (int index = 0; index < size; index++)
            {
                if (VertexShaderRegisterIndex >= 0)
                    effect.Device.SetVertexShaderConstantFloat4(VertexShaderRegisterIndex + index, ref data[index]);
                if (PixelShaderRegisterIndex >= 0)
                    effect.Device.SetPixelShaderConstantFloat4(PixelShaderRegisterIndex + index, ref data[index]);
            }

            // Take ownership on registers
            if (VertexShaderRegisterIndex >= 0)
                EffectCacheSystem.TakeOwnershipOfVertexRegisters(effect,VertexShaderRegisterIndex, size);
            if (PixelShaderRegisterIndex >= 0)
                EffectCacheSystem.TakeOwnershipOfPixelRegisters(effect, PixelShaderRegisterIndex, size);

            isDirty = false;
        }
    }
}
